﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LoanMessages;
using System.Threading;
using PServiceBus.Services.Gateway.Runtime;
using PServiceBus.MessageProxy.Interface;
using PServiceBus.MessageProxy;
using PServiceBus.Transports;

namespace LoanBrokerService {

    public class LoanBrokerServer : IESBMessage<LoanRequest, LoanResponse>,
        IESBMessage<CreditBureauRequest, CreditBureauReply>,
        IESBMessage<BankQuoteRequest, BankQuoteReply>
    {
        static Random _random = new Random();
        static int _quoteCounter = 0;

        private ILoanService service;

        const int MAX_LOAN_TERM = 75;
        const double PRIME_RATE = 4.40;
        const double RATE_PREMIUM = 10.55;

        public LoanBrokerServer() {
            service = ProxyService.Create<ILoanService>();
        }

        [ESBTransport(typeof(TcpTransport))]
        [ESBTransportParam("Port", "4444")]
        [ESBTransportParam("IPAddress", "127.0.0.1")]
        public LoanResponse Handle(LoanRequest msg) {
            Program._bankResponses.Clear();
            Program._loanRequest = msg;

            Console.WriteLine("Received Loan Request: Amount: {0}, Term: {1}",
                msg.LoanAmount, msg.LoanTerm);

            service.SendCreditRequest(new CreditBureauRequest() { SSN = msg.SSN });
            Program._reset.WaitOne();
            Program._reset.Reset();
            var response = new LoanResponse();
            var bestBankQuote = Program._bankResponses.Where(quote => quote.InterestRate <= Program._bankResponses.Min(b => b.InterestRate)).FirstOrDefault();
            if (bestBankQuote != null) {
                response.InterestRate = bestBankQuote.InterestRate;
                response.ErrorCode = bestBankQuote.ErrorCode;
                response.QuoteID = bestBankQuote.QuoteID;
                response.Completed = true;
            }
            Console.WriteLine("Finished");
            return response;
        }

        [ESBTransport(typeof(TcpTransport))]
        [ESBTransportParam("Port", "5555")]
        [ESBTransportParam("IPAddress", "127.0.0.1")]
        public CreditBureauReply Handle(CreditBureauRequest msg) {
            Console.WriteLine("Received Request for Credit [SSN = {0}]", msg.SSN);
            var response = new CreditBureauReply();
            response.SSN = msg.SSN;
            response.CreditScore = (int)(_random.Next(600) + 300);
            response.HistoryLength = (int)(_random.Next(19) + 1);
            return response;
        }

        [ESBTransport(typeof(TcpTransport))]
        [ESBTransportParam("Port", "6666")]
        [ESBTransportParam("IPAddress", "127.0.0.1")]
        public BankQuoteReply Handle(BankQuoteRequest msg) {
            Console.WriteLine("Received request for SSN {0} for {1:c} / {2} months",
                    msg.SSN, msg.LoanAmount, msg.LoanTerm);

            var response = new BankQuoteReply();

            if (msg.LoanTerm <= MAX_LOAN_TERM) {
                response.InterestRate = PRIME_RATE + RATE_PREMIUM
                                         + (double)(msg.LoanTerm / 12) / 10
                                         + (double)_random.Next(10) / 10;
                response.ErrorCode = 0;
            } else {
                response.InterestRate = 0.0;
                response.ErrorCode = 1;
            }
            response.QuoteID = String.Format("{0}-{1:00000}", msg.BankName, ++_quoteCounter);

            Console.WriteLine("  Quote: {0} {1} {2}",
                              response.ErrorCode, response.InterestRate, response.QuoteID);
            return response;
        }

    }

    class Program {
        public static LoanRequest _loanRequest;
        public static List<BankQuoteReply> _bankResponses = new List<BankQuoteReply>();
        public static ManualResetEvent _reset = new ManualResetEvent(false);
        static string[] _banks = new[] { "First Bank", "England Bank", "WellsFargo" };
        static int _bankCount = _banks.Length;
        static ILoanService _service;

        static void Main(string[] args) {

            ProxyService.Start();

            _service = ProxyService.Create<ILoanService>();

            _reset.Reset();
            Console.WriteLine("Waiting for Loan Request.... Press enter to exit");

            _service.ReceiveBankQuote(response => {
                _bankResponses.Add(response);
                if (_bankResponses.Count >= _bankCount) _reset.Set();
                Console.WriteLine("Received Bank Quote for Quote#: {0}", response.QuoteID);
            }, ex => true);

            _service.ReceiveCreditQuote(response =>
            {
                Console.WriteLine("ReceiveCreditQuote: LoanAmount: {0}, LoanTerm: {1}",
                    _loanRequest.LoanAmount, _loanRequest.LoanTerm);
                Console.WriteLine("Received Credit Response [SSN = {0}, CreditScore = {1}, HistoryLength = {2}]",
                    response.SSN, response.CreditScore, response.HistoryLength);
                foreach (var bank in _banks) {
                    _service.SendBankQuote(new BankQuoteRequest()
                    { 
                        BankName = bank,
                        CreditScore = response.CreditScore,
                        HistoryLength = response.HistoryLength,
                        LoanAmount = _loanRequest.LoanAmount,
                        LoanTerm = _loanRequest.LoanTerm,
                        SSN = response.SSN
                    });
                }
            }, ex => true);
           

            Console.ReadLine();

            ProxyService.Shutdown();
        }
    }
}
